package cena;

import java.util.Random;

import javax.media.opengl.GL;

import persistencia.GerenciadorLabirinto;
import caracteristicas.Camera;
import caracteristicas.Transformacao;

import com.sun.opengl.util.GLUT;

import constantes.Constantes;

public class Labirinto {

	private byte matrizLabirinto[][];
	private Transformacao transformacao;

	private short comprimentoLabirinto;
	private short alturaLabirinto;
	private float larguraParede;

	private Camera camera;

	private Ponto posicaoPersonagem;
	private Ponto pontoInicio;
	private Ponto pontoFinal;

	private GerenciadorLabirinto gerenciador;

	public Labirinto(GL gl, GLUT glut) {

		gerarLabirinto();
		larguraParede = 1.0f;
		transformacao = new Transformacao();
		inicializarCamera(gl, glut);
	}

	private void gerarLabirinto() {

		gerenciador = new GerenciadorLabirinto();
		Random geradorAleatorio = new Random();

		short idLabirinto = (short) geradorAleatorio.nextInt(5);

		short altura = gerenciador.getAlturaLabirinto(idLabirinto);
		short comprimento = gerenciador.getComprimentoLabirinto(idLabirinto);
		System.out.println(idLabirinto);
		Ponto pontoInicial = gerenciador.getPontoInicialLabirinto(idLabirinto);
		System.out.println(pontoInicial.getX());
		Ponto pontofinal = gerenciador.getPontoFinalLabirinto(idLabirinto);

		matrizLabirinto = gerenciador.retornaLabirinto(idLabirinto);
		setAlturaLabirinto(altura);
		setComprimentoLabirinto(comprimento);
		setPontoInicio(pontoInicial);
		setPontoFinal(pontofinal);
		setPosicaoPersonagem(pontoInicial);
	}

	private void inicializarCamera(GL gl, GLUT glut) {
		
		Ponto posicaoInicialCamera = alterarCoordenadasPonto(pontoInicio);
		System.out.println(pontoInicio.getX());
		Ponto cameraOlhandoPara = new Ponto(10, 0, 10);

		camera = new Camera(posicaoInicialCamera, cameraOlhandoPara);
	}

	public void desenharLabirinto(GL gl, GLUT glut) {

		desenharChaoLabirinto(gl);
		Ponto ponto;

		for (short i = 0; i < getComprimentoLabirinto(); i++) {
			for (short j = 0; j < getAlturaLabirinto(); j++) {

				if (matrizLabirinto[i][j] == Constantes.PAREDE_LABIRINTO) {
					ponto = new Ponto(i * larguraParede, j * larguraParede);
					desenharBlocoLabirinto(ponto, gl, glut);
				}
			}
		}
		
		desenharPersonagem(gl, glut);

	}

	private void desenharBlocoLabirinto(Ponto ponto, GL gl, GLUT glut) {

		gl.glPushMatrix();
		gl.glMultMatrixd(transformacao.GetDate(), 0);
		transformacao.limparMatriz();
		matrizTranslacaoLabirinto(ponto);
		gl.glColor3f(1.0f, 0.0f, 0.0f);
		glut.glutSolidCube(larguraParede);
		gl.glPopMatrix();
	}

	private void desenharChaoLabirinto(GL gl) {

		gl.glColor3f(0.0f, 0.0f, 0.0f);
		gl.glBegin(GL.GL_QUADS);
		gl.glVertex3f(0.0f, 0.0f, 0.0f);
		gl.glVertex3f(getComprimentoLabirinto(), 0.0f, 0.0f);
		gl.glVertex3f(getComprimentoLabirinto(), getAlturaLabirinto(), 0.0f);
		gl.glVertex3f(0.0f, getAlturaLabirinto(), 0.0f);
		gl.glEnd();
	}

	private void desenharPersonagem(GL gl, GLUT glut) {
		gl.glPushMatrix();
		gl.glMultMatrixd(transformacao.GetDate(), 0);
		transformacao.limparMatriz();
		matrizTranslacaoLabirinto(posicaoPersonagem);
		gl.glColor3f(0.0f, 1.0f, 0.0f);
		glut.glutSolidCube(larguraParede);
		gl.glPopMatrix();
	}

	private Transformacao matrizTranslacao(Ponto ponto) {

		Transformacao matrixTranslacao = new Transformacao();
		matrixTranslacao.realizarTranslacao(ponto);
		return matrixTranslacao;
	}

	public void matrizTranslacaoLabirinto(Ponto ponto) {
		transformacao = transformacao
				.transformarMatriz(matrizTranslacao(ponto));
	}

	public boolean movimentarPersonagem(byte direcao) {

		Ponto posicaoPersonagem = proximoPosicaoPersonagem(direcao);
		boolean chegouFinal = false;

		if (posicaoPersonagem != null) {

			if (personagemPodeIr(posicaoPersonagem)) {
				chegouFinal = alterarPosicaoPersonagem(posicaoPersonagem,
						direcao);
			}
		}
		return chegouFinal;
	}

	private boolean alterarPosicaoPersonagem(Ponto novaPosicao, byte direcao) {

		setPosicaoPersonagem(novaPosicao);
		camera.moverCameraPara(alterarCoordenadasPonto(novaPosicao));

		if (pontoEstaBordaLabirinto(novaPosicao)) {
			camera.olharPara(alterarCoordenadasPonto(novaPosicao));
		} else {
			camera.olharPara(alterarCoordenadasPonto(proximoPosicaoPersonagem(direcao)));
		}
		return novaPosicao.verificarIguldadePontos(pontoFinal);
	}

	private Ponto alterarCoordenadasPonto(Ponto ponto) {

		Ponto pontoAlterado = new Ponto();
		pontoAlterado.setX(ponto.getX());
		pontoAlterado.setY(1);
		pontoAlterado.setZ(ponto.getY());

		return pontoAlterado;

	}

	private Ponto proximoPosicaoPersonagem(byte direcao) {

		Ponto novaPosicaoPersonagem = null;
		short novoX = 0;
		short novoY = 0;

		switch (direcao) {
		case Constantes.DIRECAO_CIMA:
			System.out.println("CIMA " + getPosicaoPersonagem().getY());
			if ((getPosicaoPersonagem().getY() - 1) >= 0) {
				novoX = (short) getPosicaoPersonagem().getX();
				novoY = (short) (getPosicaoPersonagem().getY() - 1);
				novaPosicaoPersonagem = new Ponto(novoX, novoY);
			}
			break;

		case Constantes.DIRECAO_BAIXO:

			if ((getPosicaoPersonagem().getY() + 1) < getAlturaLabirinto()) {
				novoX = (short) getPosicaoPersonagem().getX();
				novoY = (short) (getPosicaoPersonagem().getY() + 1);
				novaPosicaoPersonagem = new Ponto(novoX, novoY);
			}
			break;

		case Constantes.DIRECAO_DIREITA:

			if ((getPosicaoPersonagem().getX() + 1) < getComprimentoLabirinto()) {
				novoX = (short) (getPosicaoPersonagem().getX() + 1);
				novoY = (short) getPosicaoPersonagem().getY();
				novaPosicaoPersonagem = new Ponto(novoX, novoY);
			}
			break;

		case Constantes.DIRECAO_ESQUERDA:

			if ((getPosicaoPersonagem().getX() - 1) >= 0) {
				novoX = (short) (getPosicaoPersonagem().getX() - 1);
				novoY = (short) getPosicaoPersonagem().getY();
				novaPosicaoPersonagem = new Ponto(novoX, novoY);
			}
			break;

		}
		return novaPosicaoPersonagem;
	}

	private boolean personagemPodeIr(Ponto novaPosicao) {

		short XArrendondado = (short) novaPosicao.getX();
		short YArrendondado = (short) novaPosicao.getY();
		return matrizLabirinto[XArrendondado][YArrendondado] != Constantes.PAREDE_LABIRINTO;
	}

	private boolean pontoEstaBordaLabirinto(Ponto posicao) {
		return posicao.getX() == 0
				|| posicao.getX() == (getComprimentoLabirinto() - 1)
				|| posicao.getY() == 0
				|| posicao.getY() == (getAlturaLabirinto() - 1);
	}

	public Ponto posicaoCentralLabirinto() {

		short altura = getAlturaLabirinto();
		short comprimento = getComprimentoLabirinto();

		return new Ponto(comprimento / 2, altura / 2, 0);
		// return new Ponto(comprimento / 2, 1, altura / 2);
	}

	public Camera getCameraPersonagem() {
		return camera;
	}

	public Ponto getPosicaoPersonagem() {
		return posicaoPersonagem;
	}

	public void setPosicaoPersonagem(Ponto posicaoPersonagem) {
		this.posicaoPersonagem = posicaoPersonagem;
	}

	public short getComprimentoLabirinto() {
		return comprimentoLabirinto;
	}

	public void setComprimentoLabirinto(short comprimentoLabirinto) {
		this.comprimentoLabirinto = comprimentoLabirinto;
	}

	public short getAlturaLabirinto() {
		return alturaLabirinto;
	}

	public void setAlturaLabirinto(short alturaLabirinto) {
		this.alturaLabirinto = alturaLabirinto;
	}

	public Ponto getPontoInicio() {
		return pontoInicio;
	}

	public void setPontoInicio(Ponto pontoInicio) {
		this.pontoInicio = pontoInicio;
	}

	public Ponto getPontoFinal() {
		return pontoFinal;
	}

	public void setPontoFinal(Ponto pontoFinal) {
		this.pontoFinal = pontoFinal;
	}

}
